package mmc.server.shell.ops.utility;

import java.lang.reflect.Constructor;
import java.util.Arrays;

import mmc.EngineObject;
import mmc.server.Server;
import mmc.server.shell.ShellException;
import mmc.server.shell.ops.Function;
import mmc.server.shell.ops.Operation;
import mmc.server.shell.ops.constant.StringValue;

public class Spawn extends Function {

    public Spawn(String text) throws ShellException {
        super(text, "spawn", 1, true);
    }

    @SuppressWarnings("unchecked")
	@Override
    public double execute() {
    	Class<? extends EngineObject> clasz;
    	
    	try{
    		String cname = ((StringValue)arguments.getArgument(0)).text;
    		
    		if(!cname.contains("."))
    			cname = "mmc.objects."+cname;
    		
    		clasz = (Class<? extends EngineObject>) Class.forName(cname);
		}catch(Exception e){		
			throw new RuntimeException("Spawn: first argument must be a valid class name ("+e.getMessage()+")");
		}
    	
		Class<?>[] classes = new Class<?>[arguments.getArguments().size()-1];
		Object[] args = new Object[arguments.getArguments().size()-1];
		
        for (int i=1;i<arguments.getArguments().size();i++){
        	Operation a = arguments.getArguments().get(i);
        	
        	if(a instanceof StringValue){
        		args[i-1] = ((StringValue)a).text;
        		classes[i-1] = args[i-1].getClass();
        	}else{
        		args[i-1] = new Float(a.execute());
        		classes[i-1] = float.class;
        	}
        }

        //search perfect match
        try{
        	Server.instance.getScene().addObject(clasz.getConstructor(classes).newInstance(args));
        	return 0;
        }catch(Exception e){}
        
        //try to cast where needed
        try{
        	for(Constructor<?> c : clasz.getConstructors()){
        		if(c.getParameterTypes().length == args.length){
        			try{
	        			Object[] nargs = new Object[args.length];
	        			
	        			for(int i=0;i<nargs.length;i++){
	        				if(c.getParameterTypes()[i].equals(int.class)){
	        					nargs[i] = (int)(float)(Float)args[i];
	        				}else if(c.getParameterTypes()[i].equals(char.class)){
	        					nargs[i] = (char)(float)(Float)args[i];
	        				}else if(c.getParameterTypes()[i].equals(byte.class)){
	        					nargs[i] = (byte)(float)(Float)args[i];
	        				}else if(c.getParameterTypes()[i].equals(double.class)){
	        					nargs[i] = (double)(float)(Float)args[i];
	        				}else if(c.getParameterTypes()[i].equals(short.class)){
	        					nargs[i] = (short)(float)(Float)args[i];
	        				}else if(c.getParameterTypes()[i].equals(long.class)){
	        					nargs[i] = (long)(float)(Float)args[i];
	        				}else{
	        					nargs[i] = args[i];
	        				}
	        			}
	        			
	        			Server.instance.getScene().addObject((EngineObject) c.newInstance(nargs));
	        			return 0;
        			}catch(Exception e){
        				e.printStackTrace(System.out);
        			}
        		}
        	}
        }catch(Exception e){}
        
        //try auto
        try{
        	Server.instance.getScene().addObject(clasz.getConstructor(classes).newInstance(args));
        }catch(Exception e){
        	throw new RuntimeException("could not create "+clasz.getSimpleName()+" using "+Arrays.toString(args)+" ("+Arrays.toString(classes)+")",e);
        }
        
        return 0;
    }
    
    public static String getDescription(){
    	return formatHelpString("spawn(className,constructor-args)","spawns an entity");
    }

}
